package board;

import java.awt.*;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.*;

import sgf.SGFParseException;
import sgf.SGFUtils;
import ui.GobanPanel;
import util.Util;

/**
 * Represents a mark on the Board
 * @author TKington
 *
 */
public abstract class NodeMark implements Comparable<NodeMark> {
	/** A circle */
	public static final int CIR = 4;
	/** A triangle */
    public static final int TRI = 5;
    /** A square */
    public static final int SQU = 6;
    /** An X */
    public static final int X = 7;
    /** Black dots representing black territory */
    public static final int TERRB = 8;
    /** White dots representing white territory */
    public static final int TERRW = 9;
    /** Gray square on the board */
    public static final int GREYSQ = 10;
    /** Gray stone */
    public static final int GREYSTONE = 12;
    /** Green triangle */
    public static final int GREENTRI = 13;
    /** Red triangle */
    public static final int REDTRI = 14;
    /** Green circle */
    public static final int GREENCIR = 15;
    /** Red circle */
    public static final int REDCIR = 16;
    /** Label */
    public static final int LABEL = 17;
    /** Line */
    public static final int LINE = 18;
    
    /** The mark type */
    protected int type;
    
    /**
     * Returns the mark type.
     * @return the mark type
     */
    public int getType() { return type; }
    
    /**
     * Returns the mark type for an SGF tag.
     * @param t the tag
     * @return the mark type
     */
    public static int getSimpleMarkType(String t) {
        if(t.equals("SQ")) //$NON-NLS-1$
            return SQU;
        if(t.equals("CR")) //$NON-NLS-1$
            return CIR;
        if(t.equals("TR")) //$NON-NLS-1$
            return TRI;
        if(t.equals("MA")) //$NON-NLS-1$
            return X;
        if(t.equals("TB")) //$NON-NLS-1$
            return TERRB;
        if(t.equals("TW")) //$NON-NLS-1$
            return TERRW;
        if(t.equals("TT")) //$NON-NLS-1$
            return GREYSQ;
        if(t.equals("XD") || t.equals("XZ")) //$NON-NLS-1$ //$NON-NLS-2$
            return GREYSTONE;
        return -1;
    }
    
    /**
     * Creates NodeMark objects from an SGF tag and property values.
     * @param marks a list to hold the newly created marks
     * @param tag the SGF tag
     * @param propVals a list of property values
     * @return true if the SGF tag was recognized
     * @throws SGFParseException if the property values are illegal
     */
    public static boolean create(ArrayList<NodeMark> marks, String tag,
                                 ArrayList propVals) throws SGFParseException {
        int type = NodeMark.getSimpleMarkType(tag);
        
        if(type != -1) {
            for(int i = 0; i < propVals.size(); i++) {
            	ArrayList pts = SGFUtils.stringToPoints((String)propVals.get(i));
            	for(int j = 0; j < pts.size(); j++) {
            		marks.add(new SimpleMark(type, (Point)pts.get(j)));
            	}
            }
            return true;
        }
        
        if(tag.equals("LB") || tag.equals("XA")) { //$NON-NLS-1$ //$NON-NLS-2$
            for(int i = 0; i < propVals.size(); i++) {
                String [] t = ((String)propVals.get(i)).split(":"); //$NON-NLS-1$
                String label;
                if(t.length == 1)
                    label = String.valueOf(i + 1);
                else label = t[1];
                marks.add(new NodeLabel(label, SGFUtils.stringToPoint(t[0])));
            }
            return true;
        }
        
        if(tag.equals("LN") || tag.equals("LR") || tag.equals("LS")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
            for(int i = 0; i < propVals.size(); i++) {
                String [] t = ((String)propVals.get(i)).split(":"); //$NON-NLS-1$
                marks.add(new LineMark(tag, SGFUtils.stringToPoint(t[0]), SGFUtils.stringToPoint(t[1])));
            }
            return true;
        }
        
        if(tag.equals("XX") || tag.equals("XY")) { //$NON-NLS-1$ //$NON-NLS-2$
            String label = tag.substring(1,2);
            for(int i = 0; i < propVals.size(); i++) {
                marks.add(new NodeLabel(label, SGFUtils.stringToPoint((String)propVals.get(i))));
            }
            return true;
        }
        
        return false;
    }
    
	public int compareTo(NodeMark m) {
		return type - m.type;
	}
	
    @Override
	public abstract Object clone();
    @Override
	public abstract boolean equals(Object o);
    
    /**
     * Checks if the NodeMark fits on a board of the given size.
     * @param size the Board size
     * @throws SGFParseException if the NodeMark is off the Board
     */
    public abstract void validatePoints(int size) throws SGFParseException;
    
    /**
     * Causes the GobanPanel to repaint.
     * @param panel the GobanPanel
     */
    public abstract void invalidatePanel(GobanPanel panel);
    
    /**
     * Gets the bounding Rectangle for this NodeMark.
     * @return the bounding Rectangle
     */
    public abstract Rectangle getBounds();

    /** The file format revision. */
    private static final int REVISION = 1;
    
    /**
     * Reads a NodeMark from a file.
     * @param in the input stream
     * @return the new NodeMark object
     * @throws IOException if there is a problem reading the file
     * @throws ClassNotFoundException if there is a problem with the file format
     */
	public static NodeMark readFromFile(ObjectInputStream in)
								throws IOException, ClassNotFoundException {
		int revision = in.readInt();
		if(revision > REVISION)
			throw new IOException("File is newer than program");
		
		int type = in.readInt();
		Point p = (Point)in.readObject();

		if(type == LABEL) {
			String text = (String)in.readObject();
			try {
				return new NodeLabel(text, p);
			}
			catch(SGFParseException e) {
				Util.log(e);
			}
		}
		
		return new SimpleMark(type, p);
	}
	
	/**
	 * Writes a NodeMark to a file.
	 * @param out the output stream
	 * @param mark the NodeMark object
	 * @throws IOException if there is a problem writing to the file
	 */
	public static void writeToFile(ObjectOutputStream out, NodeMark mark) throws IOException {
		out.writeInt(REVISION);
		out.writeInt(mark.getType());
		
		SimpleMark sm = (SimpleMark)mark;
		out.writeObject(sm.getPoint());
		if(mark.getType() == LABEL)
			out.writeObject(((NodeLabel)sm).getText());
	}
}
